package sf.database.meta;

import sf.core.DBCascadeField;
import sf.core.DBField;
import sf.database.annotations.ColumnInfo;
import sf.database.annotations.FetchDBField;
import sf.database.annotations.SavedDefaultValue;
import sf.database.annotations.Type;
import sf.database.annotations.UniqueKeyGenerator;
import sf.database.dbinfo.ColumnDBType;
import sf.database.jdbc.sql.IdentifierGenerator;
import sf.database.jdbc.type.TypeHandler;
import sf.tools.reflect.PropertyHold;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.ElementCollection;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.MapKeyClass;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.MapKeyJoinColumns;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.Transient;

/**
 * 对应的java实体类的字段
 * @author sxf
 */
public class ColumnMapping implements IColumnMapping {
    /**
     * 原始的ColumnName(对应数据库)
     */
    protected String rawColumnName;
    /**
     * 特殊字符的转换,
     */
    protected transient String cachedEscapeColumnName;
    protected transient String lowerColumnName;// 数据库列名，小写，不转义
    protected transient String upperColumnName;// 数据库列名，大写，不转义

    /**
     *
     */
    private TypeHandler<Object> handler;
    // 对应java类
    protected TableMapping meta;
    /**
     * java字段名
     */
    private String fieldName;
    protected DBField field;// field对象
    protected DBCascadeField cascadeField;//级联对象,描述类中的级联关系(一对一,一对多,多对多,多对一)
    protected ColumnDBType columnDef;// 数据库定义
    /**
     * 得到java类型
     */
    protected Class<?> clz;
    private boolean pk;// 是否为主键
    protected PropertyHold fieldAccessor;// 获得字段访问器


    private int sqlType;// 对应java.sql.Types的值

    private boolean notInsert;
    private boolean notUpdate;
    private boolean tail;//是否支持tail,将未匹配的属性写入Map中

    private boolean createdDate;//是否是创建时间
    private boolean lastModifiedDate;//是否是修改时间

    //自定义注解
    private ColumnInfo columnInfo;
    private Type type;
    private FetchDBField fetchDBField;
    private String comment;// 注释
    private UniqueKeyGenerator uniqueKeyGenerator;//自定义主键生成策略
    private IdentifierGenerator identifierGenerator;//和上面的主键策略合用.
    private SavedDefaultValue saveDefaultValue;//是否使用对象的默认值

    //JPA标准注解
    private Column column;
    private GeneratedValue gv;
    private TableGenerator tableGenerator;
    private SequenceGenerator sequenceGenerator;

    //是否有Version标记,主要是乐观锁功能.对应@see javax.persistence.Version
    private boolean version;
    private boolean lob;// 是否是lob类型
    private Temporal temporal;// 日期处理
    private Enumerated enumerated;// 枚举处理
    private OrderBy orderBy;
    private OrderColumn orderColumn;
    private Transient jpaTransient;// 是否是transient
    private Convert convert;
    private ElementCollection elementCollection;
    private CollectionTable collectionTable;
    //以下为map映射处理,map映射只支持查询,不支持修改和插入
    private MapKey mapKey;
    private MapKeyClass mapKeyClass;
    private MapKeyColumn mapKeyColumn;
    private MapKeyEnumerated mapKeyEnumerated;
    private MapKeyJoinColumn mapKeyJoinColumn;
    private MapKeyJoinColumns mapKeyJoinColumns;

    // 表间关系处理
    private OneToMany oneToMany;
    private ManyToMany manyToMany;
    private OneToOne oneToOne;
    private ManyToOne manyToOne;

    private JoinColumn joinColumn;
    private JoinColumns joinColumns;
    private JoinTable joinTable;

    /**
     * 级联关系配置,只有处于级联关系下才有值
     */
    private CascadeConfig cascadeConfig;

    /**
     * 是否自增
     * @return
     */
    public boolean isAuto() {
        if (!(gv != null &&
                ((gv.strategy() == GenerationType.IDENTITY || gv.strategy() == GenerationType.SEQUENCE)
                        && Number.class.isAssignableFrom(clz)))) {
            return true;
        }
        return false;
    }

    /**
     * 是否为数字自增
     * @return
     */
    public boolean isAutoNumber() {
        if (!(gv != null &&
                ((gv.strategy() == GenerationType.AUTO ||
                        gv.strategy() == GenerationType.IDENTITY ||
                        gv.strategy() == GenerationType.SEQUENCE) &&
                        Number.class.isAssignableFrom(clz)))) {
            return true;
        }
        return false;
    }


    /**
     * 是否是级联字段
     * @return
     */
    public boolean isCascade() {
        return oneToMany != null || oneToOne != null || manyToMany != null || manyToOne != null;
    }

    public String getRawColumnName() {
        return rawColumnName;
    }

    public void setRawColumnName(String rawColumnName) {
        this.rawColumnName = rawColumnName;
    }

    public String getCachedEscapeColumnName() {
        return cachedEscapeColumnName;
    }

    public void setCachedEscapeColumnName(String cachedEscapeColumnName) {
        this.cachedEscapeColumnName = cachedEscapeColumnName;
    }

    public String getLowerColumnName() {
        return lowerColumnName;
    }

    public void setLowerColumnName(String lowerColumnName) {
        this.lowerColumnName = lowerColumnName;
    }

    public String getUpperColumnName() {
        return upperColumnName;
    }

    public void setUpperColumnName(String upperColumnName) {
        this.upperColumnName = upperColumnName;
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public DBField getField() {
        return field;
    }

    public void setField(DBField field) {
        this.field = field;
    }

    public DBCascadeField getCascadeField() {
        return cascadeField;
    }

    public void setCascadeField(DBCascadeField cascadeField) {
        this.cascadeField = cascadeField;
    }

    public Class<?> getClz() {
        return clz;
    }

    public void setClz(Class<?> clz) {
        this.clz = clz;
    }

    @Override
    public boolean isPk() {
        return pk;
    }

    @Override
    public void setPk(boolean pk) {
        this.pk = pk;
    }

    @Override
    public PropertyHold getFieldAccessor() {
        return fieldAccessor;
    }

    public void setFieldAccessor(PropertyHold fieldAccessor) {
        this.fieldAccessor = fieldAccessor;
    }

    @Override
    public TableMapping getMeta() {
        return meta;
    }

    public void setMeta(TableMapping meta) {
        this.meta = meta;
    }

    public Column getColumn() {
        return column;
    }

    public void setColumn(Column column) {
        this.column = column;
    }

    public GeneratedValue getGv() {
        return gv;
    }

    public void setGv(GeneratedValue gv) {
        this.gv = gv;
    }

    @Override
    public boolean isLob() {
        return lob;
    }

    public void setLob(boolean lob) {
        this.lob = lob;
    }

    public Temporal getTemporal() {
        return temporal;
    }

    public void setTemporal(Temporal temporal) {
        this.temporal = temporal;
    }

    public Enumerated getEnumerated() {
        return enumerated;
    }

    public void setEnumerated(Enumerated enumerated) {
        this.enumerated = enumerated;
    }

    public int getSqlType() {
        return sqlType;
    }

    public void setSqlType(int sqlType) {
        this.sqlType = sqlType;
    }

    public TypeHandler<Object> getHandler() {
        return handler;
    }

    public void setHandler(TypeHandler<Object> handler) {
        this.handler = handler;
    }

    public OneToMany getOneToMany() {
        return oneToMany;
    }

    public void setOneToMany(OneToMany oneToMany) {
        this.oneToMany = oneToMany;
    }

    public ManyToMany getManyToMany() {
        return manyToMany;
    }

    public void setManyToMany(ManyToMany manyToMany) {
        this.manyToMany = manyToMany;
    }

    public OneToOne getOneToOne() {
        return oneToOne;
    }

    public void setOneToOne(OneToOne oneToOne) {
        this.oneToOne = oneToOne;
    }

    public ManyToOne getManyToOne() {
        return manyToOne;
    }

    public void setManyToOne(ManyToOne manyToOne) {
        this.manyToOne = manyToOne;
    }

    public JoinColumn getJoinColumn() {
        return joinColumn;
    }

    public void setJoinColumn(JoinColumn joinColumn) {
        this.joinColumn = joinColumn;
    }

    public JoinColumns getJoinColumns() {
        return joinColumns;
    }

    public void setJoinColumns(JoinColumns joinColumns) {
        this.joinColumns = joinColumns;
    }

    public JoinTable getJoinTable() {
        return joinTable;
    }

    public void setJoinTable(JoinTable joinTable) {
        this.joinTable = joinTable;
    }

    public boolean isNotInsert() {
        return notInsert;
    }

    @Override
    public boolean isGenerated() {
        return false;
    }

    public void setNotInsert(boolean notInsert) {
        this.notInsert = notInsert;
    }

    public boolean isNotUpdate() {
        return notUpdate;
    }

    public void setNotUpdate(boolean notUpdate) {
        this.notUpdate = notUpdate;
    }

    public ColumnInfo getColumnInfo() {
        return columnInfo;
    }

    public void setColumnInfo(ColumnInfo columnInfo) {
        this.columnInfo = columnInfo;
    }

    public ColumnDBType getColumnDef() {
        return columnDef;
    }

    public void setColumnDef(ColumnDBType columnDef) {
        this.columnDef = columnDef;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public SequenceGenerator getSequenceGenerator() {
        return sequenceGenerator;
    }

    public void setSequenceGenerator(SequenceGenerator sequenceGenerator) {
        this.sequenceGenerator = sequenceGenerator;
    }

    public TableGenerator getTableGenerator() {
        return tableGenerator;
    }

    public void setTableGenerator(TableGenerator tableGenerator) {
        this.tableGenerator = tableGenerator;
    }

    public OrderBy getOrderBy() {
        return orderBy;
    }

    public void setOrderBy(OrderBy orderBy) {
        this.orderBy = orderBy;
    }

    public Transient getJpaTransient() {
        return jpaTransient;
    }

    public void setJpaTransient(Transient jpaTransient) {
        this.jpaTransient = jpaTransient;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public CascadeConfig getCascadeConfig() {
        return cascadeConfig;
    }

    public void setCascadeConfig(CascadeConfig cascadeConfig) {
        this.cascadeConfig = cascadeConfig;
    }

    public FetchDBField getFetchDBField() {
        return fetchDBField;
    }

    public void setFetchDBField(FetchDBField fetchDBField) {
        this.fetchDBField = fetchDBField;
    }

    public UniqueKeyGenerator getUniqueKeyGenerator() {
        return uniqueKeyGenerator;
    }

    public void setUniqueKeyGenerator(UniqueKeyGenerator uniqueKeyGenerator) {
        this.uniqueKeyGenerator = uniqueKeyGenerator;
    }

    public IdentifierGenerator getIdentifierGenerator() {
        return identifierGenerator;
    }

    public void setIdentifierGenerator(IdentifierGenerator identifierGenerator) {
        this.identifierGenerator = identifierGenerator;
    }

    public boolean isVersion() {
        return version;
    }

    public void setVersion(boolean version) {
        this.version = version;
    }

    public Convert getConvert() {
        return convert;
    }

    public void setConvert(Convert convert) {
        this.convert = convert;
    }

    public OrderColumn getOrderColumn() {
        return orderColumn;
    }

    public void setOrderColumn(OrderColumn orderColumn) {
        this.orderColumn = orderColumn;
    }

    public MapKey getMapKey() {
        return mapKey;
    }

    public void setMapKey(MapKey mapKey) {
        this.mapKey = mapKey;
    }

    public MapKeyClass getMapKeyClass() {
        return mapKeyClass;
    }

    public void setMapKeyClass(MapKeyClass mapKeyClass) {
        this.mapKeyClass = mapKeyClass;
    }

    public MapKeyColumn getMapKeyColumn() {
        return mapKeyColumn;
    }

    public void setMapKeyColumn(MapKeyColumn mapKeyColumn) {
        this.mapKeyColumn = mapKeyColumn;
    }

    public MapKeyEnumerated getMapKeyEnumerated() {
        return mapKeyEnumerated;
    }

    public void setMapKeyEnumerated(MapKeyEnumerated mapKeyEnumerated) {
        this.mapKeyEnumerated = mapKeyEnumerated;
    }

    public MapKeyJoinColumn getMapKeyJoinColumn() {
        return mapKeyJoinColumn;
    }

    public void setMapKeyJoinColumn(MapKeyJoinColumn mapKeyJoinColumn) {
        this.mapKeyJoinColumn = mapKeyJoinColumn;
    }

    public MapKeyJoinColumns getMapKeyJoinColumns() {
        return mapKeyJoinColumns;
    }

    public void setMapKeyJoinColumns(MapKeyJoinColumns mapKeyJoinColumns) {
        this.mapKeyJoinColumns = mapKeyJoinColumns;
    }

    public ElementCollection getElementCollection() {
        return elementCollection;
    }

    public void setElementCollection(ElementCollection elementCollection) {
        this.elementCollection = elementCollection;
    }

    public CollectionTable getCollectionTable() {
        return collectionTable;
    }

    public void setCollectionTable(CollectionTable collectionTable) {
        this.collectionTable = collectionTable;
    }

    public boolean isTail() {
        return tail;
    }

    public void setTail(boolean tail) {
        this.tail = tail;
    }

    public SavedDefaultValue getSaveDefaultValue() {
        return saveDefaultValue;
    }

    public void setSaveDefaultValue(SavedDefaultValue saveDefaultValue) {
        this.saveDefaultValue = saveDefaultValue;
    }

    public boolean isCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(boolean createdDate) {
        this.createdDate = createdDate;
    }

    public boolean isLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(boolean lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ColumnMapping that = (ColumnMapping) o;
        if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) return false;
        return true;
    }

    @Override
    public int hashCode() {
        int result = fieldName != null ? fieldName.hashCode() : 0;
        result = 31 * result;
        return result;
    }

    @Override
    public String toString() {
        return "ColumnMapping{" +
                "fieldName='" + fieldName + '\'' +
                '}';
    }
}
